////////////////////////////////////////////////////////////////////////////////
// Copyright 2006 CORE Solutions Limited
// All rights reserved.
// PROPRIETARY AND CONFIDENTIAL INFORMATION.
// Use is subject to license terms.
//
// FUNCTION ID      :   
// AUTHOR           :    
// DATE CREATE      :   
// DESCRIPTION      :   
//                  :   
//                  :   
//                  :   
//
// MODIFICATION HISTORY:
// DATE         AUTHOR          VERSION NO.         COMMENT
// -----------  --------------  ------------------  ----------------------------
// 2006/04/26   Ric Yik         1.0                 BugZilla #5535
//
//
////////////////////////////////////////////////////////////////////////////////
package com.erp.framework.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;

import com.erp.app.common.ProjectConstants;
import com.erp.framework.SimpleConstants;
import com.erp.framework.model.schema.MetaData;
import com.erp.framework.model.schema.TableSchema;
import com.erp.framework.util.logger.Logger;

public class BeanUtil implements SimpleConstants{

    public static Object copyPropertiesDefault(Object dest, Object src) {
        try {
            BeanUtil.copyProperties(dest, src);
            return dest;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e.getMessage());
        } catch (InvocationTargetException e) {
            Logger.logExceptions(CommonUtil.getTargetExecption(e));
            throw new RuntimeException(e.getMessage());
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e.getMessage());
        }
    } 

    public static Object getPropertyDefault(Object object, String property) {
        if (object instanceof Map) return ((Map) object).get(property);
        try {
            return PropertyUtils.getProperty(object, property);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e.getMessage());
        } catch (InvocationTargetException e) {
            Logger.logExceptions(object, CommonUtil.getTargetExecption(e));
            throw new RuntimeException(e.getMessage());
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e.getMessage());
        }
    } 

    public static void setPropertyDefault(Object object, String property, Object value) {
        if (object instanceof Map) {
            ((Map) object).put(property, value);
        } else {
            try {
                PropertyUtils.setProperty(object, property, value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e.getMessage());
            } catch (InvocationTargetException e) {
                Logger.logExceptions(object, CommonUtil.getTargetExecption(e));
                throw new RuntimeException(e.getMessage());
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e.getMessage());
            }
        }
    } 

    public static void copyProperties(Object dest, Object src) throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (dest == null)
            throw new IllegalArgumentException("No destination bean specified");
        if (src == null)
            throw new IllegalArgumentException("No source bean specified");

        if (ConvertUtil.lookup(java.util.Date.class) != ConvertUtil.dateConverter) {
            ConvertUtil.register(ConvertUtil.dateConverter, java.util.Date.class);
        }

        PropertyDescriptor srcDescriptors[] = PropertyUtils.getPropertyDescriptors(src);
        for (int i = 0; i < srcDescriptors.length; i++) {
            String name = srcDescriptors[i].getName();
            Class srcType = PropertyUtils.getPropertyType(src, name);
            Class destType = null;

            if (name.equals("class"))
                continue;

            PropertyDescriptor destinationProperty = PropertyUtils.getPropertyDescriptor(dest, name);
            if (destinationProperty != null && destinationProperty.getWriteMethod() != null) {
                destType = PropertyUtils.getPropertyType(dest, name); // return
                try {
                    if (srcType == destType) {
                        Object srcValue = PropertyUtils.getSimpleProperty(src, name);
                        PropertyUtils.setSimpleProperty(dest, name, srcValue);
                    } else if (srcType == String.class) {
                        String srcValue = (String) PropertyUtils.getSimpleProperty(src, name);
                        Object destValue = ConvertUtil.convert(srcValue, destType);
                        PropertyUtils.setSimpleProperty(dest, name, destValue);
                    } else if (destType == String.class) {
                        Object srcValue = PropertyUtils.getSimpleProperty(src, name);
                        String destValue = (srcType == java.util.Date.class) ? CommonUtil.dateToString((java.util.Date) srcValue)
                                : ConvertUtil.convert(srcValue);
                        PropertyUtils.setSimpleProperty(dest, name, destValue);
                    } else {
                        Object srcValue = PropertyUtils.getSimpleProperty(src, name);
                        String stringValue = ConvertUtil.convert(srcValue);
                        Object destValue = ConvertUtil.convert(stringValue, destType);
                        PropertyUtils.setSimpleProperty(dest, name, destValue);
                    }
                } catch (NoSuchMethodException e) {
                	throw e;
                } catch (Exception e) {
                    Logger.logExceptions(e);
                }
            }
        }
    }

    public static void decodeProperties(Object obj, String enc) throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (obj == null)
            throw new IllegalArgumentException("No obj bean specified");
        if (enc == null)
            throw new IllegalArgumentException("Encoding not specified");

        PropertyDescriptor objDescriptors[] = PropertyUtils.getPropertyDescriptors(obj);
        for (int i = 0; i < objDescriptors.length; i++) {
            String name = objDescriptors[i].getName();
            Class propType = objDescriptors[i].getPropertyType();
            if (propType == null)
                continue;
            if (propType == String.class) {
                String value = (String) PropertyUtils.getSimpleProperty(obj, name);
                try {
                    PropertyUtils.setSimpleProperty(obj, name, CommonUtil.decodeString(value, enc));
                } catch (NoSuchMethodException e) {
                	throw e; //Skip non-matching property
                }
            } else if (propType == String[].class) {
                Object tmpObj = PropertyUtils.getSimpleProperty(obj, name);
                if (tmpObj != null) {
                    String[] values = (String[]) tmpObj;
                    for (int j = 0; j < values.length; j++) {
                        values[j] = CommonUtil.decodeString(values[j], enc);
                    }
                    PropertyUtils.setSimpleProperty(obj, name, values);
                }
            }
        }
    }

    public static void setStringPropertiesEmpty(Object obj) throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (obj == null)
            throw new IllegalArgumentException("No obj bean specified");

        PropertyDescriptor objDescriptors[] = PropertyUtils.getPropertyDescriptors(obj);
        for (int i = 0; i < objDescriptors.length; i++) {
            String name = objDescriptors[i].getName();
            Class propType = objDescriptors[i].getPropertyType();
            if (propType == null)
                continue;
            if (propType == String.class) {
                try {
                    PropertyUtils.setSimpleProperty(obj, name, "");
                } catch (NoSuchMethodException e) {
                	throw e; // Skip non-matching property
                }
            } else if (propType == String[].class) {
                Object tmpObj = PropertyUtils.getSimpleProperty(obj, name);
                if (tmpObj != null) {
                    String[] values = (String[]) tmpObj;
                    for (int j = 0; j < values.length; j++) {
                        values[j] = "";
                    }
                    PropertyUtils.setSimpleProperty(obj, name, values);
                }
            }
        }
    }

    public static void setSpecificStringPropertiesEmpty(Object obj, String startStr) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {

        if (obj == null)
            throw new IllegalArgumentException("No obj bean specified");

        PropertyDescriptor objDescriptors[] = PropertyUtils.getPropertyDescriptors(obj);
        for (int i = 0; i < objDescriptors.length; i++) {
            String name = objDescriptors[i].getName();
            Class propType = objDescriptors[i].getPropertyType();
            if (propType == null)
                continue;

            if (name != null) {
                if (startStr != null && !startStr.trim().equals("")) {
                    if (!name.startsWith(startStr)) {
                        continue;
                    }
                }
            }

            if (propType == String.class) {
                try {
                    PropertyUtils.setSimpleProperty(obj, name, "");
                } catch (NoSuchMethodException e) {
                	throw e; // Skip non-matching property
                }
            } else if (propType == String[].class) {
                Object tmpObj = PropertyUtils.getSimpleProperty(obj, name);
                if (tmpObj != null) {
                    String[] values = (String[]) tmpObj;
                    for (int j = 0; j < values.length; j++) {
                        values[j] = "";
                    }
                    PropertyUtils.setSimpleProperty(obj, name, values);
                }
            }
        }
    }

    public static boolean isIdentical(Object src, Object dest) throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        return isIdentical(src, dest, null);
    }

    public static boolean isIdentical(Object src, Object dest, ArrayList ignoreList) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {
        boolean identical = true;
        try {
            PropertyDescriptor srcDescriptors[] = PropertyUtils.getPropertyDescriptors(src);
            for (int i = 0; i < srcDescriptors.length; i++) {
                String name = srcDescriptors[i].getName();

                // Poor style! You are hardcoding an exception here
                if (name.equals("parent"))
                    continue;

                // past in ignore param list
                boolean flagContinue = false;
                for (int k = 0; ignoreList != null && k < ignoreList.size(); k++) {
                    if (name.equals(ignoreList.get(k).toString()))
                        flagContinue = true;
                }
                if (flagContinue)
                    continue;

                if (PropertyUtils.getPropertyDescriptor(dest, name) != null) {
                    Object srcValue = PropertyUtils.getSimpleProperty(src, name);
                    Object destValue = PropertyUtils.getSimpleProperty(dest, name);
                    if (srcValue == null && destValue == null)
                        continue;

                    if ((srcValue == null || destValue == null) || !srcValue.toString().equals(destValue.toString())) {
                        identical = false;
                        break;
                    }
                }
            }
        } catch (Exception e) {
            Logger.logExceptions(e);
            return false;
        }
        return identical;
    }

    public static boolean isIdenticalBySpecificFields(Object src, Object dest, ArrayList includeList) throws IllegalAccessException,
    InvocationTargetException, NoSuchMethodException {
		boolean identical = true;
		try {
		    for (int i = 0; i < includeList.size(); i++) {
		    	String name = includeList.get(i).toString();
		        if (PropertyUtils.getPropertyDescriptor(dest, name) != null) {
		            Object srcValue = PropertyUtils.getSimpleProperty(src, name);
		            Object destValue = PropertyUtils.getSimpleProperty(dest, name);
		            if (srcValue == null && destValue == null)
		                continue;
		
		            if ((srcValue == null || destValue == null) || !srcValue.toString().equals(destValue.toString())) {
		                identical = false;
		                break;
		            }
		        }
		    }
		} catch (Exception e) {
		    Logger.logExceptions(e);
		    return false;
		}
		return identical;
    }
	
    public static boolean isIdenticalBySpecificField(Object src, Object dest, String propertyName) throws IllegalAccessException,
    InvocationTargetException, NoSuchMethodException {
		boolean identical = true;
		try {
	        if (PropertyUtils.getPropertyDescriptor(dest, propertyName) != null) {
	            Object srcValue = PropertyUtils.getSimpleProperty(src, propertyName);
	            Object destValue = PropertyUtils.getSimpleProperty(dest, propertyName);
	            if (srcValue == null && destValue == null){
	            	identical = true;
	            }else if((srcValue == null || destValue == null) || !srcValue.toString().equals(destValue.toString())) {
	                identical = false;
	            }
	        }
		} catch (Exception e) {
		    Logger.logExceptions(e);
		    return false;
		}
		return identical;
    }
    
    public static boolean isSpecificPrefixPropertyIdentical(Object src, String srcPrefix, Object dest, String destPrefix,
            boolean nullEqualsToEmpty) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        boolean result = false;

        try {
            result = isSpecificPrefixPropertyIdentical(src, srcPrefix, dest, destPrefix, nullEqualsToEmpty, null);
        } catch (Exception e) {
            Logger.logExceptions(e);
        }

        return result;
    }

    public static boolean isSpecificPrefixPropertyIdentical(Object src, String srcPrefix, Object dest, String destPrefix,
            boolean nullEqualsToEmpty, String[] excludeField) throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        boolean identical = true;

        if (dest == null)
            throw new IllegalArgumentException("No destination bean specified");

        if (src == null)
            throw new IllegalArgumentException("No source bean specified");

        destPrefix = CommonUtil.nullToString(destPrefix);
        srcPrefix = CommonUtil.nullToString(srcPrefix);

        try {
            PropertyDescriptor srcDescriptors[] = PropertyUtils.getPropertyDescriptors(src);
            for (int i = 0; i < srcDescriptors.length; i++) {
                String srcName = srcDescriptors[i].getName();
                String destName = "";
                boolean exclude = false;

                if (!srcPrefix.equals("")) {
                    if (!srcName.startsWith(srcPrefix)) {
                        continue;
                    }
                }

                String name = srcName;
                String tempStr = "";

                if (!srcPrefix.equals("")) {
                    tempStr = name.substring(srcPrefix.length(), srcPrefix.length() + 1);
                    name = name.substring(srcPrefix.length() + 1);
                    name = tempStr.toLowerCase() + name;
                }

                if (!destPrefix.equals("")) {
                    tempStr = name.substring(0, 1);
                    name = name.substring(srcPrefix.length() + 1);
                    name = destPrefix + tempStr.toUpperCase() + name;
                }

                destName = name;

                // check exclude field
                for (int j = 0; excludeField != null && j < excludeField.length; j++) {
                    if (name.equals(excludeField[j])) {
                        exclude = true;
                        break;
                    }
                }

                if (exclude) {
                    continue;
                }

                if (PropertyUtils.getPropertyDescriptor(dest, destName) != null) {
                    Object srcValue = PropertyUtils.getSimpleProperty(src, srcName);
                    Object destValue = PropertyUtils.getSimpleProperty(dest, destName);

                    if (srcValue == null && destValue == null)
                        continue;

                    if (nullEqualsToEmpty) {
                        if (srcValue == null) {
                            if (!destValue.toString().equals("")) {
                                identical = false;
                                break;
                            }
                        } else if (destValue == null) {
                            if (!srcValue.toString().equals("")) {
                                identical = false;
                                break;
                            }
                        } else {
                            if (!srcValue.toString().equals(destValue.toString())) {
                                identical = false;
                                break;
                            }
                        }
                    } else {
                        if ((srcValue == null || destValue == null) || !srcValue.toString().equals(destValue.toString())) {
                            identical = false;
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            Logger.logExceptions(e);
            return false;
        }
        return identical;
    }

    public static void escapeSearchStrings(Object obj) {
        if (obj == null)
            return;

        escapeStrings(obj, ProjectConstants.SEARCH_CRIT_PREFIX);
    }

    private static void escapeStrings(Object obj, String prefix) {
        PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(obj);
        PropertyDescriptor desc = null;
        Class clazz = null;
        String str = "";

        for (int i = 0; i < descriptors.length; i++) {
            desc = descriptors[i];
            clazz = desc.getPropertyType();

            if (clazz != null && clazz.getName().equals("java.lang.String")) {
                if (desc.getName().startsWith(prefix))
                    try {
                        str = (String) PropertyUtils.getSimpleProperty(obj, desc.getName());
                        str = CommonUtil.sqlEncode(str);
                        PropertyUtils.setSimpleProperty(obj, desc.getName(), str);
                    } catch (IllegalAccessException e) {
                        Logger.logExceptions(e);
                    } catch (InvocationTargetException e) {
                        Logger.logExceptions(e);
                    } catch (NoSuchMethodException e) {
                        Logger.logExceptions(e);
                    } catch (ClassCastException e) {
                        Logger.logExceptions(e);
                    }
            }
        }
    }

    public static String toPropertyName(String prefix, String propertyName) {
        if (CommonUtil.isEmpty(prefix)) {
            return ((propertyName.length() > 1) ? propertyName.substring(0, 1).toLowerCase() 
                    + propertyName.substring(1) : propertyName.substring(0, 1).toLowerCase());
        } else {
            propertyName = ((propertyName.length() > 1) ? propertyName.substring(0, 1).toUpperCase() 
                    + propertyName.substring(1) : propertyName.substring(0, 1).toUpperCase());
            return prefix + propertyName;
        }
    }

    public static String toPropertyName(String propertyName) {
        return toPropertyName("", propertyName);
    }

    public static Object getConstant(String className, String constant) {
        try {
            return getConstant(Class.forName(className), constant);
        } catch (Exception e) {
            throw new RuntimeException("Error on getting constant " + constant + ": " + e.getMessage());
        }
    }

    public static Object getConstant(Class c, String constant) {
        try {
            return c.getField(constant).get(null);
        } catch (Exception e) {
            throw new RuntimeException("Error on getting constant " + constant + ": " + e.getMessage());
        }
    }

    public static void setConstant(Class c, String constant, Object value) {
        try {
            c.getField(constant).set(null, value);
        } catch (Exception e) {
            throw new RuntimeException("Error on getting constant " + constant + ": " + e.getMessage());
        }
    }

    public static MetaData getMetaData(Object object, String property) {
        if (object == null || CommonUtil.isEmpty(property)) throw new IllegalArgumentException("object must not be null and property must not be empty");
        int lastIndexOfDot = property.lastIndexOf('.');
        Object bean = object;
        if (lastIndexOfDot != -1) {
            bean = getPropertyDefault(object, property.substring(0, lastIndexOfDot));
        }
        TableSchema ts = (TableSchema) getPropertyDefault(bean, "ts");
        String voName = property.substring(lastIndexOfDot + 1);
        int lastIndexOfIndexQualifier = voName.lastIndexOf("[");
        if (lastIndexOfIndexQualifier != -1) {
            voName = voName.substring(0, lastIndexOfIndexQualifier);
        }
        return (ts == null) ? null : ts.getMetaDataByVoName(voName);
    }

}
